import numpy as np
import matplotlib

matplotlib.use('TkAgg')  # 设置Matplotlib后端为TkAgg
import matplotlib.pyplot as plt
import requests


# 步骤 1：加载并预处理数据
def load_and_preprocess_data(url, columns, classes):
    """
    加载数据并进行预处理，包括筛选特定类别和标准化。

    参数:
    url: 数据文件的URL
    columns: 列名列表
    classes: 需要保留的类别标签

    返回:
    X_scaled: 标准化后的特征数据
    y_filtered: 筛选后的类别标签
    """
    # 使用requests获取数据
    response = requests.get(url)
    if response.status_code != 200:
        raise Exception(f"Failed to fetch data from {url}. Status code: {response.status_code}")

    # 将文本数据转换为NumPy数组
    lines = response.text.strip().split('\n')
    data = np.array([list(map(float, line.split(','))) for line in lines])

    # 提取特征和类别标签
    y = data[:, 0].astype(int)
    X = data[:, 1:]

    # 筛选类别为1和2的数据
    mask = np.isin(y, classes)
    X_filtered = X[mask]
    y_filtered = y[mask]

    # 数据标准化
    mean = np.mean(X_filtered, axis=0)
    std = np.std(X_filtered, axis=0, ddof=0)  # 使用总体标准差
    X_scaled = (X_filtered - mean) / std

    return X_scaled, y_filtered


# 步骤 2：定义K-Means算法
class KMeans:
    def __init__(self, k=2, max_iters=100, tol=1e-4):
        self.k = k
        self.max_iters = max_iters
        self.tol = tol
        self.centroids = None
        self.labels = None

    def fit(self, X):
        """
        训练K-Means模型。

        参数:
        X: 输入数据，形状为 (样本数量, 特征数量)
        """
        # 随机初始化聚类中心
        idx = np.random.choice(X.shape[0], self.k, replace=False)
        self.centroids = X[idx].copy()

        for _ in range(self.max_iters):
            prev_centroids = self.centroids.copy()

            # 分配簇
            distances = np.linalg.norm(X[:, np.newaxis] - self.centroids, axis=2)
            self.labels = np.argmin(distances, axis=1)

            # 更新聚类中心
            for i in range(self.k):
                if np.sum(self.labels == i) > 0:  # 防止除零错误
                    self.centroids[i] = X[self.labels == i].mean(axis=0)

            # 检查收敛条件
            if np.all(np.abs(self.centroids - prev_centroids) < self.tol):
                break

    def predict(self, X):
        """
        预测新数据点的簇标签。

        参数:
        X: 输入数据，形状为 (样本数量, 特征数量)

        返回:
        labels: 预测的簇标签
        """
        distances = np.linalg.norm(X[:, np.newaxis] - self.centroids, axis=2)
        return np.argmin(distances, axis=1)


# 步骤 3：运行K-Means算法并可视化结果
if __name__ == "__main__":
    # 设置Matplotlib支持中文显示（如果你的环境中没有SimHei字体，请根据实际情况调整）
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # 加载数据
    url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"
    columns = ['Class', 'Alcohol', 'Malic_acid', 'Ash', 'Alcalinity_of_ash', 'Magnesium',
               'Total_phenols', 'Flavanoids', 'Nonflavanoid_phenols', 'Proanthocyanins',
               'Color_intensity', 'Hue', 'OD280/OD315', 'Proline']
    X_scaled, y_filtered = load_and_preprocess_data(url, columns, [1, 2])

    # 初始化并训练K-Means模型
    kmeans = KMeans(k=2)
    kmeans.fit(X_scaled)

    # 可视化聚类结果（这里只展示前两个特征）
    plt.figure(figsize=(8, 6))
    plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=kmeans.labels, cmap='viridis', edgecolor='k', s=50)
    plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], c='red', marker='X', s=200, label='聚类中心')
    plt.title('K-Means 聚类结果')
    plt.xlabel('酒精含量 (Alcohol)')
    plt.ylabel('苹果酸含量 (Malic acid)')
    plt.legend()

    # 保存图像而不是显示
    plt.savefig("kmeans_result.png")
    print("图像已保存为 kmeans_result.png")